package com.gitee.wsl.compose.chart.geometry.model

/*
 * Copyright 2010, 2011, 2012 mapsforge.org
 * Copyright 2013 Hannes Janetzek
 *
 * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
 *
 * This program is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * A tag represents an immutable key-value pair. Keys are always intern().
 */
class Tag {
    /**
     * The key of this tag.
     */
    val key: String?

    /**
     * The value of this tag.
     */
    var value: String?

    /**
     * true when value is intern().
     */
    private val intern: Boolean
    private var hashCodeValue = 0

    /**
     * @param key
     * the key of the tag.
     * @param value
     * the value of the tag.
     */
    constructor(key: String?, value: String?) {
        this.key = key
        this.value = value
        intern = true
    }

    /**
     * Create Tag with interned Key.
     *
     * @param key
     * the key of the tag.
     * @param value
     * the value of the tag.
     * @param internValue
     * true when value string should be intern()alized.
     */
    constructor(key: String?, value: String?, internValue: Boolean) {
        this.key = key
        this.value = if (value == null || !internValue) value else value
        intern = internValue
    }

    constructor(key: String?, value: String?, internKey: Boolean, internValue: Boolean) {
        this.key = if (key == null || !internKey) key else key
        this.value = if (value == null || !internValue) value else value
        intern = internValue
    }

    override fun equals(obj: Any?): Boolean {
        if (this === obj) {
            return true
        } else if (obj !is Tag) {
            return false
        }
        val other = obj
        if (key !== other.key) return false
        if (intern && other.intern) {
            if (value === other.value) return true
        } else if (!intern && value == other.value) {
            return true
        }
        return false
    }

    override fun hashCode(): Int {
        if (hashCodeValue == 0) hashCodeValue = calculateHashCode()
        return hashCodeValue
    }

    override fun toString(): String {
        return StringBuilder()
            .append("Tag[")
            .append(key)
            .append(',')
            .append(value)
            .append(']')
            .toString()
    }

    /**
     * @return the hash code of this object.
     */
    private fun calculateHashCode(): Int {
        var result = 7
        result = 31 * result + (key?.hashCode() ?: 0)
        result = 31 * result + if (value == null) 0 else value.hashCode()
        return result
    }

    companion object {
        /** The key of the house number OpenStreetMap tag.  */
        const val KEY_HOUSE_NUMBER = "addr:housenumber"

        /** The key of the name OpenStreetMap tag.  */
        const val KEY_NAME = "name"

        /** The key of the reference OpenStreetMap tag.  */
        const val KEY_REF = "ref"

        /** The key of the elevation OpenStreetMap tag.  */
        const val KEY_ELE = "ele"
        const val KEY_AMENITY = "amenity"
        const val KEY_BUILDING = "building"
        const val KEY_HIGHWAY = "highway"
        const val KEY_LANDUSE = "landuse"
        const val KEY_HEIGHT = "height"
        const val KEY_MIN_HEIGHT = "min_height"
        const val VALUE_YES = "yes"
        const val VALUE_NO = "no"

        /**
         * @param tag
         * the textual representation of the tag.
         */
        fun parse(tag: String): Tag {
            val splitPosition = tag.indexOf('=')
            return if (splitPosition < 0) {
                Tag(tag, "")
            } else Tag(tag.substring(0, splitPosition), tag.substring(splitPosition + 1))
        }
    }
}

